from typing import Optional, List, Dict, Any, Tuple
from sqlalchemy.orm import Session
from sqlalchemy import and_, or_, desc, asc, func, text
from datetime import datetime, timedelta
import logging

from app.domains.models import Device, SensorData, WorkOrder, Alert, DeviceStatistics
from app.domains.device.schemas import (
    DeviceCreate, DeviceUpdate, DeviceListQuery, DeviceStatusUpdate,
    SensorDataQuery, DeviceBatchOperation
)
from app.shared.exceptions.base import (
    NotFoundException, ConflictException, ValidationException, BusinessException
)
from app.shared.constants.status_codes import BusinessCode
from app.shared.constants.enums import DeviceStatus, DeviceType
from app.shared.utils.datetime import utc_now
from app.shared.utils.formatter import generate_device_code

logger = logging.getLogger(__name__)


class DeviceRepository:
    """设备数据访问层"""
    
    def __init__(self, db: Session):
        self.db = db
    
    def get_by_id(self, device_id: int) -> Optional[Device]:
        """根据ID获取设备"""
        return self.db.query(Device).filter(Device.id == device_id).first()
    
    def get_by_code(self, device_code: str) -> Optional[Device]:
        """根据设备编码获取设备"""
        return self.db.query(Device).filter(Device.device_code == device_code).first()
    
    def create(self, device_data: DeviceCreate) -> Device:
        """创建设备"""
        device = Device(
            device_code=device_data.device_code,
            device_name=device_data.device_name,
            device_type=device_data.device_type,
            location_name=device_data.location_name,
            latitude=device_data.latitude,
            longitude=device_data.longitude,
            status=DeviceStatus.OFFLINE,  # 新建设备默认离线
            materials_level=100,          # 默认满载
            created_at=utc_now(),
            updated_at=utc_now()
        )
        
        self.db.add(device)
        self.db.commit()
        self.db.refresh(device)
        
        logger.info(f"✅ 设备创建成功: {device.device_code} - {device.device_name}")
        return device
    
    def update(self, device: Device, device_data: DeviceUpdate) -> Device:
        """更新设备信息"""
        update_data = device_data.dict(exclude_unset=True)
        
        for field, value in update_data.items():
            setattr(device, field, value)
        
        device.updated_at = utc_now()
        
        self.db.commit()
        self.db.refresh(device)
        
        logger.info(f"✅ 设备信息更新成功: {device.device_code}")
        return device
    
    def update_status(self, device: Device, status_data: DeviceStatusUpdate) -> Device:
        """更新设备状态"""
        device.status = status_data.status
        
        if status_data.materials_level is not None:
            device.materials_level = status_data.materials_level
        
        device.updated_at = utc_now()
        
        self.db.commit()
        self.db.refresh(device)
        
        logger.info(f"✅ 设备状态更新: {device.device_code} -> {status_data.status}")
        return device
    
    def get_list(self, query: DeviceListQuery) -> Tuple[List[Device], int]:
        """获取设备列表"""
        # 构建查询
        q = self.db.query(Device)
        
        # 搜索条件
        if query.search:
            search_term = f"%{query.search}%"
            q = q.filter(or_(
                Device.device_code.like(search_term),
                Device.device_name.like(search_term),
                Device.location_name.like(search_term)
            ))
        
        # 设备类型筛选
        if query.device_type:
            q = q.filter(Device.device_type == query.device_type)
        
        # 设备状态筛选
        if query.status:
            q = q.filter(Device.status == query.status)
        
        # 位置筛选
        if query.location:
            q = q.filter(Device.location_name.like(f"%{query.location}%"))
        
        # 耗材余量筛选
        if query.materials_level_lt is not None:
            q = q.filter(Device.materials_level < query.materials_level_lt)
        
        # 排序
        sort_column = getattr(Device, query.sort_by, Device.updated_at)
        if query.sort_order == "desc":
            q = q.order_by(desc(sort_column))
        else:
            q = q.order_by(asc(sort_column))
        
        # 获取总数
        total = q.count()
        
        # 分页
        offset = (query.page - 1) * query.page_size
        devices = q.offset(offset).limit(query.page_size).all()
        
        return devices, total
    
    def delete(self, device: Device) -> bool:
        """删除设备"""
        try:
            self.db.delete(device)
            self.db.commit()
            
            logger.info(f"✅ 设备删除成功: {device.device_code}")
            return True
        except Exception as e:
            logger.error(f"❌ 设备删除失败: {e}")
            self.db.rollback()
            return False
    
    def get_device_statistics(self, device_id: int) -> Dict[str, Any]:
        """获取设备统计信息"""
        # 工单统计
        work_orders_count = self.db.query(WorkOrder).filter(WorkOrder.device_id == device_id).count()
        
        # 未解决告警统计
        alerts_count = self.db.query(Alert).filter(
            Alert.device_id == device_id,
            Alert.is_resolved == False
        ).count()
        
        # 最近7天的传感器数据
        recent_sensor_data = self.db.query(SensorData).filter(
            SensorData.device_id == device_id,
            SensorData.timestamp >= utc_now() - timedelta(days=7)
        ).order_by(desc(SensorData.timestamp)).limit(100).all()
        
        return {
            'work_orders_count': work_orders_count,
            'alerts_count': alerts_count,
            'recent_sensor_data': [
                {
                    'timestamp': data.timestamp,
                    'data_type': data.data_type,
                    'value': data.value,
                    'unit': data.unit
                } for data in recent_sensor_data
            ]
        }
    
    def get_sensor_data(self, query: SensorDataQuery) -> Tuple[List[SensorData], int]:
        """获取传感器数据"""
        q = self.db.query(SensorData).filter(SensorData.device_id == query.device_id)
        
        # 数据类型筛选
        if query.data_type:
            q = q.filter(SensorData.data_type == query.data_type)
        
        # 时间范围筛选
        if query.start_time:
            q = q.filter(SensorData.timestamp >= query.start_time)
        
        if query.end_time:
            q = q.filter(SensorData.timestamp <= query.end_time)
        
        # 按时间倒序排列
        q = q.order_by(desc(SensorData.timestamp))
        
        # 获取总数
        total = q.count()
        
        # 分页
        offset = (query.page - 1) * query.page_size
        sensor_data = q.offset(offset).limit(query.page_size).all()
        
        return sensor_data, total
    
    def get_devices_by_ids(self, device_ids: List[int]) -> List[Device]:
        """根据ID列表获取设备"""
        return self.db.query(Device).filter(Device.id.in_(device_ids)).all()
    
    def get_online_devices(self) -> List[Device]:
        """获取在线设备"""
        return self.db.query(Device).filter(Device.status == DeviceStatus.ONLINE).all()
    
    def get_offline_devices(self, timeout_minutes: int = 10) -> List[Device]:
        """获取离线设备"""
        timeout_time = utc_now() - timedelta(minutes=timeout_minutes)
        return self.db.query(Device).filter(
            or_(
                Device.status == DeviceStatus.OFFLINE,
                Device.last_seen_at < timeout_time
            )
        ).all()
    
    def get_low_materials_devices(self, threshold: int = 20) -> List[Device]:
        """获取耗材不足的设备"""
        return self.db.query(Device).filter(
            Device.materials_level < threshold
        ).all()


class DeviceService:
    """设备业务逻辑层"""
    
    def __init__(self, db: Session):
        self.db = db
        self.repository = DeviceRepository(db)
    
    def create_device(self, device_data: DeviceCreate) -> Device:
        """创建设备"""
        # 检查设备编码是否已存在
        if self.repository.get_by_code(device_data.device_code):
            raise ConflictException(f"设备编码 {device_data.device_code} 已存在")
        
        return self.repository.create(device_data)
    
    def get_device_by_id(self, device_id: int) -> Device:
        """根据ID获取设备"""
        device = self.repository.get_by_id(device_id)
        if not device:
            raise NotFoundException("设备", device_id)
        return device
    
    def get_device_by_code(self, device_code: str) -> Device:
        """根据设备编码获取设备"""
        device = self.repository.get_by_code(device_code)
        if not device:
            raise NotFoundException("设备")
        return device
    
    def update_device(self, device_id: int, device_data: DeviceUpdate) -> Device:
        """更新设备信息"""
        device = self.get_device_by_id(device_id)
        return self.repository.update(device, device_data)
    
    def update_device_status(self, device_id: int, status_data: DeviceStatusUpdate) -> Device:
        """更新设备状态"""
        device = self.get_device_by_id(device_id)
        return self.repository.update_status(device, status_data)
    
    def delete_device(self, device_id: int) -> bool:
        """删除设备"""
        device = self.get_device_by_id(device_id)
        
        # 检查是否有关联的工单
        work_orders_count = self.db.query(WorkOrder).filter(WorkOrder.device_id == device_id).count()
        if work_orders_count > 0:
            raise BusinessException(
                BusinessCode.OPERATION_FAILED,
                f"无法删除设备，存在 {work_orders_count} 个关联的工单"
            )
        
        return self.repository.delete(device)
    
    def get_device_list(self, query: DeviceListQuery) -> Tuple[List[Device], int]:
        """获取设备列表"""
        return self.repository.get_list(query)
    
    def get_device_detail(self, device_id: int) -> Dict[str, Any]:
        """获取设备详细信息"""
        device = self.get_device_by_id(device_id)
        stats = self.repository.get_device_statistics(device_id)
        
        return {
            "device": device,
            "statistics": stats
        }
    
    def get_sensor_data(self, query: SensorDataQuery) -> Tuple[List[SensorData], int]:
        """获取传感器数据"""
        # 验证设备是否存在
        self.get_device_by_id(query.device_id)
        return self.repository.get_sensor_data(query)
    
    async def control_device(self, device_id: int, commands: List[dict], operator: str = "system") -> bool:
        """控制设备"""
        device = self.get_device_by_id(device_id)
        
        # 检查设备是否在线
        if device.status == DeviceStatus.OFFLINE:
            raise BusinessException(
                BusinessCode.DEVICE_OFFLINE,
                f"设备 {device.device_name} 离线，无法执行控制指令"
            )
        
        # 检查设备是否故障
        if device.status == DeviceStatus.FAULT:
            raise BusinessException(
                BusinessCode.DEVICE_FAULT,
                f"设备 {device.device_name} 故障，无法执行控制指令"
            )
        
        try:
            # 通过MQTT发送控制指令
            from app.core.mqtt.client import mqtt_manager
            
            topic_location = device.location_name.replace(' ', '_') if device.location_name else 'unknown'
            
            success = await mqtt_manager.publish_device_command(
                location=topic_location,
                device_id=device.device_code,
                commands=commands,
                operator=operator
            )
            
            if success:
                logger.info(f"✅ 设备控制指令发送成功: {device.device_code}")
                return True
            else:
                logger.error(f"❌ 设备控制指令发送失败: {device.device_code}")
                return False
                
        except Exception as e:
            logger.error(f"❌ 设备控制异常: {e}")
            raise BusinessException(
                BusinessCode.OPERATION_FAILED,
                "设备控制指令发送失败"
            )
    
    def batch_operation(self, operation_data: DeviceBatchOperation) -> Dict[str, Any]:
        """批量操作设备"""
        devices = self.repository.get_devices_by_ids(operation_data.device_ids)
        
        if len(devices) != len(operation_data.device_ids):
            found_ids = [d.id for d in devices]
            missing_ids = [id for id in operation_data.device_ids if id not in found_ids]
            raise ValidationException(f"设备不存在: {missing_ids}")
        
        results = {
            "success": [],
            "failed": [],
            "total": len(devices)
        }
        
        for device in devices:
            try:
                if operation_data.operation == "update_status":
                    status = DeviceStatus(operation_data.params.get("status"))
                    status_data = DeviceStatusUpdate(status=status)
                    self.repository.update_status(device, status_data)
                    results["success"].append(device.id)
                    
                elif operation_data.operation == "delete":
                    # 检查工单关联
                    work_orders_count = self.db.query(WorkOrder).filter(WorkOrder.device_id == device.id).count()
                    if work_orders_count == 0:
                        self.repository.delete(device)
                        results["success"].append(device.id)
                    else:
                        results["failed"].append({
                            "device_id": device.id,
                            "reason": f"存在 {work_orders_count} 个关联工单"
                        })
                        
                else:
                    results["failed"].append({
                        "device_id": device.id,
                        "reason": f"不支持的操作: {operation_data.operation}"
                    })
                    
            except Exception as e:
                results["failed"].append({
                    "device_id": device.id,
                    "reason": str(e)
                })
        
        logger.info(f"✅ 批量操作完成: 成功 {len(results['success'])}, 失败 {len(results['failed'])}")
        return results
    
    def get_dashboard_statistics(self) -> Dict[str, Any]:
        """获取仪表盘统计数据"""
        # 设备状态统计
        device_stats = self.db.query(
            Device.status,
            func.count(Device.id).label('count')
        ).group_by(Device.status).all()
        
        # 在线设备数
        online_count = sum(stat.count for stat in device_stats if stat.status == DeviceStatus.ONLINE)
        
        # 总设备数
        total_count = sum(stat.count for stat in device_stats)
        
        # 故障设备数
        fault_count = sum(stat.count for stat in device_stats if stat.status == DeviceStatus.FAULT)
        
        # 耗材不足设备数
        low_materials_count = self.db.query(Device).filter(Device.materials_level < 20).count()
        
        # 平均耗材余量
        avg_materials = self.db.query(func.avg(Device.materials_level)).filter(
            Device.materials_level.isnot(None)
        ).scalar() or 0
        
        return {
            "devices": {
                "total": total_count,
                "online": online_count,
                "offline": total_count - online_count - fault_count,
                "fault": fault_count,
                "by_status": {stat.status.value: stat.count for stat in device_stats}
            },
            "materials": {
                "low_level_devices": low_materials_count,
                "avg_level": round(float(avg_materials), 1)
            }
        }